/* eslint-disable */
/*-------------------------------------------------------------------------------------------
|     ECOTree.js
|	  author:lijw
\------------------------------------------------------------------------------------------*/

const ECONode = function (id, pid, dsc, type, typeText, wc, isEdt, w, h, c, bc, target, meta) {
  this.id = id
  this.pid = pid
  this.dsc = dsc
  // 类型
  this.type = type
  this.typeText = typeText
  // 权重
  this.wc = wc
  // 编辑
  this.isEdt = isEdt
  this.w = w
  this.h = h
  this.c = c
  this.bc = bc
  this.target = target
  this.meta = meta
  
  this.siblingIndex = 0
  this.dbIndex = 0
  
  this.XPosition = 0
  this.YPosition = 0
  this.prelim = 0
  this.modifier = 0
  this.leftNeighbor = null
  this.rightNeighbor = null
  this.nodeParent = null    
  this.nodeChildren = []
  
  this.isCollapsed = false
  this.canCollapse = false
  
  this.isSelected = false
}

ECONode.prototype._getLevel = function () {
  // if (this.nodeParent.id == -1) {return 0}
  if (this.nodeParent.id == 0) {return 0}
  else return this.nodeParent._getLevel() + 1
}

ECONode.prototype._isAncestorCollapsed = function () {
  if (this.nodeParent.isCollapsed) { return true }
  else 
  {
      if (this.nodeParent.id == -1) { return false }
      else    { return this.nodeParent._isAncestorCollapsed() }
  }
}

ECONode.prototype._setAncestorsExpanded = function () {
  if (this.nodeParent.id == -1) { return }
  else 
  {
      this.nodeParent.isCollapsed = false
      return this.nodeParent._setAncestorsExpanded() 
  }    
}

ECONode.prototype._getChildrenCount = function () {
  if (this.isCollapsed) return 0
  if(this.nodeChildren == null)
      return 0
  else
      return this.nodeChildren.length
}

ECONode.prototype._getLeftSibling = function () {
  if(this.leftNeighbor != null && this.leftNeighbor.nodeParent == this.nodeParent)
      return this.leftNeighbor
  else
      return null    
}

ECONode.prototype._getRightSibling = function () {
  if(this.rightNeighbor != null && this.rightNeighbor.nodeParent == this.nodeParent)
      return this.rightNeighbor
  else
      return null    
}

ECONode.prototype._getChildAt = function (i) {
  return this.nodeChildren[i]
}

ECONode.prototype._getChildrenCenter = function (tree) {
  // node = this._getFirstChild()
  var node = this._getFirstChild()
  // node1 = this._getLastChild()
  var node1 = this._getLastChild()
  return node.prelim + ((node1.prelim - node.prelim) + tree._getNodeSize(node1)) / 2    
}

ECONode.prototype._getFirstChild = function () {
  return this._getChildAt(0)
}

ECONode.prototype._getLastChild = function () {
  return this._getChildAt(this._getChildrenCount() - 1)
}

ECONode.prototype._drawChildrenLinks = function (tree) {
  var s = []
  var xa = 0, ya = 0, xb = 0, yb = 0, xc = 0, yc = 0, xd = 0, yd = 0
  var node1 = null
  
  switch(tree.config.iRootOrientation)
  {
      case ECOTree.RO_TOP:
          xa = this.XPosition + (this.w / 2)
          ya = this.YPosition + this.h
          break
          
      case ECOTree.RO_BOTTOM:
          xa = this.XPosition + (this.w / 2)
          ya = this.YPosition
          break
          
      case ECOTree.RO_RIGHT:
          xa = this.XPosition
          ya = this.YPosition + (this.h / 2)        
          break
          
      case ECOTree.RO_LEFT:
          xa = this.XPosition + this.w
          ya = this.YPosition + (this.h / 2)        
          break        
  }
  
  for (var k = 0; k < this.nodeChildren.length; k++)
  {
      node1 = this.nodeChildren[k]
              
      switch(tree.config.iRootOrientation)
      {
          case ECOTree.RO_TOP:
              xd = xc = node1.XPosition + (node1.w / 2)
              yd = node1.YPosition
              xb = xa
              switch (tree.config.iNodeJustification)
              {
                  case ECOTree.NJ_TOP:
                      yb = yc = yd - tree.config.iLevelSeparation / 2
                      break
                  case ECOTree.NJ_BOTTOM:
                      yb = yc = ya + tree.config.iLevelSeparation / 2
                      break
                  case ECOTree.NJ_CENTER:
                      yb = yc = ya + (yd - ya) / 2
                      break
              }
              break
              
          case ECOTree.RO_BOTTOM:
              xd = xc = node1.XPosition + (node1.w / 2)
              yd = node1.YPosition + node1.h
              xb = xa
              switch (tree.config.iNodeJustification)
              {
                  case ECOTree.NJ_TOP:
                      yb = yc = yd + tree.config.iLevelSeparation / 2
                      break
                  case ECOTree.NJ_BOTTOM:
                      yb = yc = ya - tree.config.iLevelSeparation / 2
                      break
                  case ECOTree.NJ_CENTER:
                      yb = yc = yd + (ya - yd) / 2
                      break
              }                
              break

          case ECOTree.RO_RIGHT:
              xd = node1.XPosition + node1.w
              yd = yc = node1.YPosition + (node1.h / 2)    
              yb = ya
              switch (tree.config.iNodeJustification)
              {
                  case ECOTree.NJ_TOP:
                      xb = xc = xd + tree.config.iLevelSeparation / 2
                      break
                  case ECOTree.NJ_BOTTOM:
                      xb = xc = xa - tree.config.iLevelSeparation / 2
                      break
                  case ECOTree.NJ_CENTER:
                      xb = xc = xd + (xa - xd) / 2
                      break
              }                                
              break        
              
          case ECOTree.RO_LEFT:
              xd = node1.XPosition
              yd = yc = node1.YPosition + (node1.h / 2)        
              yb = ya
              switch (tree.config.iNodeJustification)
              {
                  case ECOTree.NJ_TOP:
                      xb = xc = xd - tree.config.iLevelSeparation / 2
                      break
                  case ECOTree.NJ_BOTTOM:
                      xb = xc = xa + tree.config.iLevelSeparation / 2
                      break
                  case ECOTree.NJ_CENTER:
                      xb = xc = xa + (xd - xa) / 2
                      break
              }                                
              break                
      }        
      
      
      switch(tree.render)
      {
          case "CANVAS":
              tree.ctx.save()
              tree.ctx.strokeStyle = tree.config.linkColor
              tree.ctx.scale(tree.config.zoomRatio,tree.config.zoomRatio) // canvas zoom
              tree.ctx.beginPath()            
              switch (tree.config.linkType)
              {
                  case "M":                        
                      tree.ctx.moveTo(xa,ya)
                      tree.ctx.lineTo(xb,yb)
                      tree.ctx.lineTo(xc,yc)
                      tree.ctx.lineTo(xd,yd)                        
                      break
                      
                  case "B":
                      tree.ctx.moveTo(xa,ya)
                      tree.ctx.bezierCurveTo(xb,yb,xc,yc,xd,yd)    
                      break                    
              }
              tree.ctx.stroke()
              tree.ctx.restore()
              break
                                          
          case "VML":
              switch (tree.config.linkType)
              {
                  case "M":
                      s.push('<v:polyline points="')
                      s.push(xa + ' ' + ya + ' ' + xb + ' ' + yb + ' ' + xc + ' ' + yc + ' ' + xd + ' ' + yd)         
                      s.push('" strokecolor="'+tree.config.linkColor+'"><v:fill on="false" /></v:polyline>')                    
                      break
                  case "B":
                      s.push('<v:curve from="')
                      s.push(xa + ' ' + ya + '" control1="' + xb + ' ' + yb + '" control2="' + xc + ' ' + yc + '" to="' + xd + ' ' + yd)         
                      s.push('" strokecolor="'+tree.config.linkColor+'"><v:fill on="false" /></v:curve>')                    
                      break                    
              }
              break
              
      }            
  }    
  
  return s.join('')
}

const ECOTree = function (obj, elm) {
  this.config = {
      iMaxDepth : 100,
      iLevelSeparation : 40,
      iSiblingSeparation : 20,
      iSubtreeSeparation : 20,
      iRootOrientation : ECOTree.RO_TOP,
      iNodeJustification : ECOTree.NJ_TOP,
      topXAdjustment : 0,
      topYAdjustment : 0,        
      // render : "AUTO",
      render : "CANVAS",
      linkType : "M",
      linkColor : "blue",
      nodeColor : "#CCCCFF",
      nodeFill : ECOTree.NF_GRADIENT,
      nodeBorderColor : "blue",
      nodeSelColor : "#FFFFCC",
      levelColors : ["#5555FF","#8888FF","#AAAAFF","#CCCCFF"],
      levelBorderColors : ["#5555FF","#8888FF","#AAAAFF","#CCCCFF"],
      colorStyle : ECOTree.CS_NODE,
      useTarget : true,
      searchMode : ECOTree.SM_DSC,
      selectMode : ECOTree.SL_MULTIPLE,
      defaultNodeWidth : 200,
      defaultNodeHeight : 50,
      defaultTarget : 'javascript:void(0)',
      expandedImage : '/starmooc/starcm/plugins/ecotree/img/less.gif',
      collapsedImage : '/starmooc/starcm/plugins/ecotree/img/plus.gif',
      // transImage : './img/trans.gif',
      transImage : '',
      zoomRatio:1
      ,
      canvasWidth:900,
      canvasHeight:400,
      textConfig: '权重',
      weightFlag: '%'
  }
  
  this.version = "1.1"
  this.obj = obj
  // 20181220
  // ECOTree.obj = obj
  this.elm = document.getElementById(elm)
  this.self = this
  this.render = (this.config.render == "AUTO" ) ? ECOTree._getAutoRenderMode() : this.config.render
  this.ctx = null
  this.canvasoffsetTop = 0
  this.canvasoffsetLeft = 0
  
  this.maxLevelHeight = []
  this.maxLevelWidth = []
  this.previousLevelNode = []
  
  this.rootYOffset = 0
  this.rootXOffset = 0
  
  this.nDatabaseNodes = []
  this.mapIDs = {}
  
  this.root = new ECONode(-1, null, null, 2, 2)
  this.iSelectedNode = -1
  this.iLastSearch = 0
  
}

// Constant values

// Tree orientation
ECOTree.RO_TOP = 0
ECOTree.RO_BOTTOM = 1
ECOTree.RO_RIGHT = 2
ECOTree.RO_LEFT = 3

// Level node alignment
ECOTree.NJ_TOP = 0
ECOTree.NJ_CENTER = 1
ECOTree.NJ_BOTTOM = 2

// Node fill type
ECOTree.NF_GRADIENT = 0
ECOTree.NF_FLAT = 1

// Colorizing style
ECOTree.CS_NODE = 0
ECOTree.CS_LEVEL = 1

// Search method: Title, metadata or both
ECOTree.SM_DSC = 0
ECOTree.SM_META = 1
ECOTree.SM_BOTH = 2

// Selection mode: single, multiple, no selection
ECOTree.SL_MULTIPLE = 0
ECOTree.SL_SINGLE = 1
ECOTree.SL_NONE = 2


ECOTree._getAutoRenderMode = function() {
  var r = "VML"
  var is_ie6 = /msie 6\.0/i.test(navigator.userAgent)
  var is_ff = /Firefox|Chrome/i.test(navigator.userAgent)
  var is_ie9 = /msie 9\.0|msie 10\.0/i.test(navigator.userAgent)
  if (is_ff || is_ie9) r = "CANVAS"
  return r
}

// CANVAS functions...
ECOTree._roundedRect = function (ctx,x,y,width,height,radius) {
ctx.beginPath()
ctx.moveTo(x,y+radius)
ctx.lineTo(x,y+height-radius)
ctx.quadraticCurveTo(x,y+height,x+radius,y+height)
ctx.lineTo(x+width-radius,y+height)
ctx.quadraticCurveTo(x+width,y+height,x+width,y+height-radius)
ctx.lineTo(x+width,y+radius)
ctx.quadraticCurveTo(x+width,y,x+width-radius,y)
ctx.lineTo(x+radius,y)
ctx.quadraticCurveTo(x,y,x,y+radius)
ctx.fill()
ctx.stroke()
}

// 增加入参，以便保存和刷新点击该节点之前的节点数据
// ECOTree._canvasNodeClickHandler = function (tree,target,nodeid,node,e) {
ECOTree._canvasNodeClickHandler = function (tree,target,nodeid) {
  if (target != nodeid) return
  // 为失去焦点的节点赋值刷新
  var sesid = sessionStorage.getItem("stid")
  var sescont = sessionStorage.getItem("scont")
  var seswet = sessionStorage.getItem("swet")
  if(sesid && (sescont || seswet)) {
    tree.set(sesid, '', sescont, seswet)
    sessionStorage.removeItem("stid")
    sessionStorage.removeItem("scont")
    sessionStorage.removeItem("swet")
  }
  tree.selectNode(nodeid,true)
}

// Layout algorithm
ECOTree._firstWalk = function (tree, node, level) {
      var leftSibling = null
      
      node.XPosition = 0
      node.YPosition = 0
      node.prelim = 0
      node.modifier = 0
      node.leftNeighbor = null
      node.rightNeighbor = null
      tree._setLevelHeight(node, level)
      tree._setLevelWidth(node, level)
      tree._setNeighbors(node, level)
      if(node._getChildrenCount() == 0 || level == tree.config.iMaxDepth)
      {
          leftSibling = node._getLeftSibling()
          if(leftSibling != null)
              node.prelim = leftSibling.prelim + tree._getNodeSize(leftSibling) + tree.config.iSiblingSeparation
          else
              node.prelim = 0
      } 
      else
      {
          var n = node._getChildrenCount()
          for(var i = 0; i < n; i++)
          {
              var iChild = node._getChildAt(i)
              ECOTree._firstWalk(tree, iChild, level + 1)
          }

          var midPoint = node._getChildrenCenter(tree)
          midPoint -= tree._getNodeSize(node) / 2
          if(midPoint<0)midPoint=0 // 最左边的节点，可能<0，导致出界
          leftSibling = node._getLeftSibling()
          if(leftSibling != null)
          {
              node.prelim = leftSibling.prelim + tree._getNodeSize(leftSibling) + tree.config.iSiblingSeparation
              // 如果根据左边兄弟节点的位置来推算，大于当前节点计算出的位置，则当前节点需要右移，所以要计算差值
              if(node.prelim>midPoint){
                  node.modifier = node.prelim - midPoint
              }else{
                  // 如果小于当前节点计算出的位置，就别动了，否则容易导致最底层的节点，X坐标<0
                  node.modifier = 0
              }
              ECOTree._apportion(tree, node, level)
          } 
          else
          {                
              node.prelim = midPoint
          }
      }    
}

ECOTree._apportion = function (tree, node, level) {
      var firstChild = node._getFirstChild()
      var firstChildLeftNeighbor = firstChild.leftNeighbor
      var j = 1
      for(var k = tree.config.iMaxDepth - level; firstChild != null && firstChildLeftNeighbor != null && j <= k;)
      {
          var modifierSumRight = 0
          var modifierSumLeft = 0
          var rightAncestor = firstChild
          var leftAncestor = firstChildLeftNeighbor
          for(var l = 0; l < j; l++)
          {
              rightAncestor = rightAncestor.nodeParent
              leftAncestor = leftAncestor.nodeParent
              modifierSumRight += rightAncestor.modifier
              modifierSumLeft += leftAncestor.modifier
          }

          var totalGap = (firstChildLeftNeighbor.prelim + modifierSumLeft + tree._getNodeSize(firstChildLeftNeighbor) + tree.config.iSubtreeSeparation) - (firstChild.prelim + modifierSumRight)
          if(totalGap > 0)
          {
              var subtreeAux = node
              var numSubtrees = 0
              for(; subtreeAux != null && subtreeAux != leftAncestor; subtreeAux = subtreeAux._getLeftSibling())
                  numSubtrees++

              if(subtreeAux != null)
              {
                  var subtreeMoveAux = node
                  var singleGap = totalGap / numSubtrees
                  for(; subtreeMoveAux != leftAncestor; subtreeMoveAux = subtreeMoveAux._getLeftSibling())
                  {
                      subtreeMoveAux.prelim += totalGap
                      subtreeMoveAux.modifier += totalGap
                      totalGap -= singleGap
                  }

              }
          }
          j++
          if(firstChild._getChildrenCount() == 0)
              firstChild = tree._getLeftmost(node, 0, j)
          else
              firstChild = firstChild._getFirstChild()
          if(firstChild != null)
              firstChildLeftNeighbor = firstChild.leftNeighbor
      }
}

ECOTree._secondWalk = function (tree, node, level, X, Y) {
      if(level <= tree.config.iMaxDepth)
      {
          var xTmp = tree.rootXOffset + node.prelim + X
          var yTmp = tree.rootYOffset + Y
          var maxsizeTmp = 0
          var nodesizeTmp = 0
          var flag = false
          
          switch(tree.config.iRootOrientation)
          {            
              case ECOTree.RO_TOP:
              case ECOTree.RO_BOTTOM:                                        
                  maxsizeTmp = tree.maxLevelHeight[level]
                  nodesizeTmp = node.h                    
                  break

              case ECOTree.RO_RIGHT:
              case ECOTree.RO_LEFT:            
                  maxsizeTmp = tree.maxLevelWidth[level]
                  flag = true
                  nodesizeTmp = node.w
                  break
          }

          switch(tree.config.iNodeJustification)
          {
              case ECOTree.NJ_TOP:
                  node.XPosition = xTmp
                  node.YPosition = yTmp
                  break
  
              case ECOTree.NJ_CENTER:
                  node.XPosition = xTmp
                  node.YPosition = yTmp + (maxsizeTmp - nodesizeTmp) / 2
                  break
  
              case ECOTree.NJ_BOTTOM:
                  node.XPosition = xTmp
                  node.YPosition = (yTmp + maxsizeTmp) - nodesizeTmp
                  break
          }
          if(flag)
          {
              var swapTmp = node.XPosition
              node.XPosition = node.YPosition
              node.YPosition = swapTmp
          }
          switch(tree.config.iRootOrientation)
          {
              case ECOTree.RO_BOTTOM:
                  node.YPosition = -node.YPosition - nodesizeTmp
                  break
  
              case ECOTree.RO_RIGHT:
                  node.XPosition = -node.XPosition - nodesizeTmp
                  break
          }
          if(node._getChildrenCount() != 0)
              ECOTree._secondWalk(tree, node._getFirstChild(), level + 1, X + node.modifier, Y + maxsizeTmp + tree.config.iLevelSeparation)
          var rightSibling = node._getRightSibling()
          if(rightSibling != null)
              ECOTree._secondWalk(tree, rightSibling, level, X, Y)
      }    
}

ECOTree.prototype._positionTree = function () {    
  this.maxLevelHeight = []
  this.maxLevelWidth = []            
  this.previousLevelNode = []        
  ECOTree._firstWalk(this.self, this.root, 0)
  
  switch(this.config.iRootOrientation)
  {            
      case ECOTree.RO_TOP:
      case ECOTree.RO_LEFT: 
              this.rootXOffset = this.config.topXAdjustment + this.root.XPosition
              this.rootYOffset = this.config.topYAdjustment + this.root.YPosition
          break    
          
      case ECOTree.RO_BOTTOM:    
      case ECOTree.RO_RIGHT:             
              this.rootXOffset = this.config.topXAdjustment + this.root.XPosition
              this.rootYOffset = this.config.topYAdjustment + this.root.YPosition
  }    
  
  ECOTree._secondWalk(this.self, this.root, 0, 0, 0)    
}

ECOTree.prototype._setLevelHeight = function (node, level) {    
  if (this.maxLevelHeight[level] == null) 
      this.maxLevelHeight[level] = 0
  if(this.maxLevelHeight[level] < node.h)
      this.maxLevelHeight[level] = node.h    
}

ECOTree.prototype._setLevelWidth = function (node, level) {
  if (this.maxLevelWidth[level] == null) 
      this.maxLevelWidth[level] = 0
  if(this.maxLevelWidth[level] < node.w)
      this.maxLevelWidth[level] = node.w        
}

ECOTree.prototype._setNeighbors = function(node, level) {
  node.leftNeighbor = this.previousLevelNode[level]
  if(node.leftNeighbor != null)
      node.leftNeighbor.rightNeighbor = node
  this.previousLevelNode[level] = node    
}

ECOTree.prototype._getNodeSize = function (node) {
  switch(this.config.iRootOrientation)
  {
  case ECOTree.RO_TOP: 
  case ECOTree.RO_BOTTOM: 
      return node.w

  case ECOTree.RO_RIGHT: 
  case ECOTree.RO_LEFT: 
      return node.h
  }
  return 0
}

ECOTree.prototype._getLeftmost = function (node, level, maxlevel) {
  if(level >= maxlevel) return node
  if(node._getChildrenCount() == 0) return null
  
  var n = node._getChildrenCount()
  for(var i = 0; i < n; i++)
  {
      var iChild = node._getChildAt(i)
      var leftmostDescendant = this._getLeftmost(iChild, level + 1, maxlevel)
      if(leftmostDescendant != null)
          return leftmostDescendant
  }

  return null    
}

ECOTree.prototype._selectNodeInt = function (dbindex, flagToggle) {
  if (this.config.selectMode == ECOTree.SL_SINGLE)
  {
      if ((this.iSelectedNode != dbindex) && (this.iSelectedNode != -1))
      {
          this.nDatabaseNodes[this.iSelectedNode].isSelected = false
          // 选中一个时把其他置为非edit
          this.nDatabaseNodes[this.iSelectedNode].isEdt = false
      }        
      this.iSelectedNode = (this.nDatabaseNodes[dbindex].isSelected && flagToggle) ? -1 : dbindex
  }    
  this.nDatabaseNodes[dbindex].isSelected = (flagToggle) ? !this.nDatabaseNodes[dbindex].isSelected : true    
}

ECOTree.prototype._collapseAllInt = function (flag) {
  var node = null
  for (var n = 0; n < this.nDatabaseNodes.length; n++)
  { 
      node = this.nDatabaseNodes[n]
      if (node.canCollapse) node.isCollapsed = flag
  }    
  this.UpdateTree()
}

ECOTree.prototype._selectAllInt = function (flag) {
  var node = null
  for (var k = 0; k < this.nDatabaseNodes.length; k++)
  { 
      node = this.nDatabaseNodes[k]
      node.isSelected = flag
  }    
  this.iSelectedNode = -1
  this.UpdateTree()
}

ECOTree.prototype._drawTree = function () {
  var s = []
  var node = null
  var color = ""
  var border = ""
  
  for (var n = 0; n < this.nDatabaseNodes.length; n++)
  { 
      node = this.nDatabaseNodes[n]

      if (node.type && !node.typeText) {
          switch (node.type) {
              case 'subjective_marking':
                  node.typeText = '客观打分'
                  break;
              case 'objective_evaluation':
                  node.typeText = '主观评价'
                  break;
          
              default:
                  break;
          }
      }
      
      switch (this.config.colorStyle) {
          case ECOTree.CS_NODE:
              color = node.c
              border = node.bc
              break
          case ECOTree.CS_LEVEL:
              var iColor = node._getLevel() % this.config.levelColors.length
              color = this.config.levelColors[iColor]
              iColor = node._getLevel() % this.config.levelBorderColors.length
              border = this.config.levelBorderColors[iColor]
              break
      }
      
      if (!node._isAncestorCollapsed())
      {
          switch (this.render)
          {
              case "CANVAS":
                  // Canvas part...
                  this.ctx.save()
                  this.ctx.scale(this.config.zoomRatio,this.config.zoomRatio) // canvas zoom
                  this.ctx.strokeStyle = border
                  switch (this.config.nodeFill) {
                      case ECOTree.NF_GRADIENT:                            
                          var lgradient = this.ctx.createLinearGradient(node.XPosition,0,node.XPosition+node.w,0)
                          lgradient.addColorStop(0.0,((node.isSelected)?this.config.nodeSelColor:color))
                          lgradient.addColorStop(1.0,"#F5FFF5")
                          this.ctx.fillStyle = lgradient
                          break
                          
                      case ECOTree.NF_FLAT:
                          this.ctx.fillStyle = ((node.isSelected)?this.config.nodeSelColor:color)
                          break
                  }                    
                  
                  ECOTree._roundedRect(this.ctx,node.XPosition,node.YPosition,node.w+2,node.h,5)
                  // this.ctx.fillStyle = 'blue'
                  // this.ctx.font = 'simsun 14px'
                  // this.ctx.fillText("Hello World!",node.XPosition,node.YPosition)
                  this.ctx.restore()
                  
                  // HTML part...
                  
                  // // // 超出隐藏增加，及title,选中
                  s.push('<div id="node_' + node.id + '" class="econode" style="z-index:2;font-size:' + 14*this.config.zoomRatio + 'px;top:'+
                      (node.YPosition*this.config.zoomRatio+this.canvasoffsetTop)+'px;left:'+(node.XPosition*this.config.zoomRatio+this.canvasoffsetLeft)+
                      'px;width:'+node.w*this.config.zoomRatio+'px;height:'+node.h*this.config.zoomRatio+'px"')
                  if (this.config.selectMode != ECOTree.SL_NONE)                                            
                      // // s.push('onclick="javascript:ECOTree._canvasNodeClickHandler('+this.obj+',event.target.id,\''+node.id+'\')" ')  
                      // s.push('onclick="javascript:ECOTree._canvasNodeClickHandler('+this.obj+',\''+node.id+'\',\''+node.id+'\')" ')
                      s.push('onclick="javascript:t.selectNode(\''+node.id+'\','+true+')" ')                                 
                  s.push('>')                
                  if (node.canCollapse) {
                    // // 优化调整样式
                      s.push('<div style="float:left;margin-top:30px"><a style="" id="c' + node.id + '" href="javascript:'+this.obj+'.collapseNode(\''+node.id+'\', true)" >')
                      // s.push('<div style="float:left;margin-top:13px"><a style="" id="c' + node.id + '" href="javascript:'+this.self+'.collapseNode(\''+node.id+'\', true)" >')
                      s.push('<img border=0 src="'+((node.isCollapsed) ? this.config.collapsedImage : this.config.expandedImage)+'" >')                            
                      s.push('</a></div>')
                      // s.push('<img src="'+this.config.transImage+'" >')                        
                  }                    
                  if (node.target && this.config.useTarget)
                  {
                      s.push('<a id="t' + node.id + '" href="'+node.target+'">')
                      s.push(node.dsc)
                      s.push('</a>')
                  }                
                  else
                  {                        
                      // // s.push(node.dsc + node.XPosition)
                      // // /超出隐藏
                      if(node.isEdt) {
                          s.push('<div style="margin-left:10px;display:inline-block"><input style="width:170px" id="node_name_input_'
                                  // + node.id + '" value="' + node.dsc + '" onclick="focusInput(event)" onchange="changeContent(this)"/>')-20181220
                                  + node.id + '" value="' + node.dsc + '" onclick="t.focusInput(event)" onchange="changeContent(this)"/>')
                          if(node.canCollapse) {
                              s.push('<div id="node_weight_' + node.id + '" class="weight_div_cls">' + this.config.textConfig +
                                  '<input style="width:40px" class="node_weight_cls" onclick="t.focusInput(event)" id="node_weight_input_'
                                     // + node.id + '" value="' + node.wc + '" disabled="disabled"/>%</div></div>')暂不实现智能计算父节点权重
                                  + node.id + '" value="' + node.wc + '" onclick="t.focusInput(event)" onchange="changeWeight(this)"/>' + this.config.weightFlag +'</div></div>')
                          } else {
                              s.push('<div id="node_weight_' + node.id + '" class="weight_div_cls">' + this.config.textConfig +
                                  '<input style="width:40px" class="node_weight_cls" id="node_weight_input_'
                                     + node.id + '" value="' + node.wc + '" onclick="t.focusInput(event)" onchange="changeWeight(this)"/>' + this.config.weightFlag +'</div></div>')
                          }
                      } else {
                          // s.push('<div style="margin-left:10px;display:inline-block"><div class="over_cls" title="' + node.dsc + '">'+ node.dsc + '</div>')
                          s.push('<div style="margin-left:10px;display:inline-flex;width: 95%;"><textarea class="template_textarea_cls" disabled>'+ node.dsc + '</textarea>')
                          s.push('<div class="template_textarea_type_cls">')
                          if (node.typeText == '主观评价') {
                              s.push('<div class="template_type_cls2">评价方式：' + node.typeText + '</div></div>')
                          } else if (node.typeText == '主观评等') {
                              s.push('<div class="template_type_cls">评价方式：' + node.typeText + '</div><div id="node_weight_' + node.id + '" class="template_score_cls">权重：' + node.wc + '%</div></div>')
                          } else {
                              // 客观打分
                              s.push('<div class="template_type_cls">评价方式：' + node.typeText + '</div><div id="node_weight_' + node.id + '" class="template_score_cls">分值：' + Number(node.wc).toFixed(0) + '分</div></div>')
                          }
                          s.push('</div>')
                      }
                  }
                  s.push('</div>')        
                  break
                  
              case "VML":
                  s.push('<v:roundrect id="' + node.id + '" strokecolor="'+border+'" arcsize="0.18"    ')
                  s.push('style="position:absolute;top:'+node.YPosition+';left:'+node.XPosition+';width:'+node.w+';height:'+node.h+'" ')
                  if (this.config.selectMode != ECOTree.SL_NONE)
                      s.push('href="javascript:'+this.obj+'.selectNode(\''+node.id+'\', true)" ')                                        
                  s.push('>')
                  s.push('<v:textbox inset="0.5px,0.5px,0.5px,0.5px" ><font face=Verdana size=1>')
                  if (node.canCollapse) {
                      s.push('<a href="javascript:'+this.obj+'.collapseNode(\''+node.id+'\', true)" >')
                      s.push('<img border=0 src="'+((node.isCollapsed) ? this.config.collapsedImage : this.config.expandedImage)+'" >')                            
                      s.push('</a>')
                      s.push('<img src="'+this.config.transImage+'" >')                        
                  }                    
                  if (node.target && this.config.useTarget)
                  {
                      s.push('<a href="'+node.target+'">')
                      s.push(node.dsc)            
                      s.push('</a>')    
                  }                
                  else
                  {                        
                      s.push(node.dsc)                                    
                  }
                  s.push('</font></v:textbox>')                                            
                  switch (this.config.nodeFill) {
                      case ECOTree.NF_GRADIENT:
                          s.push('<v:fill type=gradient color2="'+((node.isSelected)?this.config.nodeSelColor:color)+'" color="#F5FFF5" angle=90 />')    
                          break
                      case ECOTree.NF_FLAT:
                          s.push('<v:fill type="solid" color="'+((node.isSelected)?this.config.nodeSelColor:color)+'" />')    
                          break
                  }
                  s.push('<v:shadow type="single" on="true" opacity="0.7" />')                    
                  s.push('</v:roundrect>')                                                                                                    
                  break
          }    
          if (!node.isCollapsed)    s.push(node._drawChildrenLinks(this.self))
      }
  }    
  return s.join('')    
}



ECOTree.prototype.toString = function () {    
  var s = []
  
  this._positionTree()
  
  switch (this.render)
  {
      case "CANVAS":
          // width,height must here.
          s.push('<canvas id="ECOTreecanvas" width=' + this.config.canvasWidth + ' height=' + this.config.canvasHeight + '></canvas>')
          break
          
      case "HTML":
          s.push('<div class="maindiv">')
          s.push(this._drawTree())
          s.push('</div>')
          break
          
      case "VML":
          s.push('<v:group coordsize="10000, 10000" coordorigin="0, 0" style="position:absolute;width=10000px;height=10000px" >')
          s.push(this._drawTree())
          s.push('</v:group>')
          break
  }
  
  return s.join('')
}

//  ECOTree API begins here...

ECOTree.prototype.UpdateTree = function () {    
  this.elm.innerHTML = this
  
  // when vml,it include text node already.
  if (this.render == "CANVAS") {
      var canvas = document.getElementById("ECOTreecanvas")
      
      // IE getContext fixed.
      if (typeof window.G_vmlCanvasManager!="undefined") { 
          canvas=window.G_vmlCanvasManager.initElement(canvas)
      }
      if (canvas && canvas.getContext)  {
          this.canvasoffsetLeft = canvas.offsetLeft
          this.canvasoffsetTop = canvas.offsetTop
          this.ctx = canvas.getContext('2d')
          var htmlTxt = this._drawTree()    
          
          if (document.createRange) {//  all browsers, except IE before version 9
              var rangeObj = this.elm.ownerDocument.createRange()
              rangeObj.setStartBefore(this.elm)
              if (rangeObj.createContextualFragment) {//  all browsers, except IE
                  
                  var documentFragment = rangeObj.createContextualFragment (htmlTxt)
                  this.elm.appendChild(documentFragment)
              }
              else { //  Internet Explorer from version 9
                  this.elm.insertAdjacentHTML ("afterBegin", htmlTxt)
              }
          }
          else { //  Internet Explorer before version 9
              this.elm.insertAdjacentHTML ("afterBegin", htmlTxt)
          }
      }
  }
}

ECOTree.prototype.add = function (id, pid, dsc, type, typeText, wc, isEdt, w, h, c, bc, target, meta) {    
  var nw = w || this.config.defaultNodeWidth // Width, height, colors, target and metadata defaults...
  var nh = h || this.config.defaultNodeHeight
  var color = c || this.config.nodeColor
  var border = bc || this.config.nodeBorderColor
  var tg = (this.config.useTarget) ? ((typeof target == "undefined") ? (this.config.defaultTarget) : target) : null
  var metadata = (typeof meta != "undefined")    ? meta : ""
  
  var pnode = null // Search for parent node in database
  // if (pid == -1) 
  if (pid == 0) 
      {
          pnode = this.root
      }
  else
      {
          for (var k = 0; k < this.nDatabaseNodes.length; k++)
          {
              if (this.nDatabaseNodes[k].id == pid)
              {
                  pnode = this.nDatabaseNodes[k]
                  break
              }
          }    
      }
  var node = new ECONode(id, pid, dsc, type, typeText, wc, isEdt, nw, nh, color, border, tg, metadata)    // New node creation...
  node.nodeParent = pnode
  // Set it's parent
  pnode.canCollapse = true
  // It's obvious that now the parent can collapse    
  var i = this.nDatabaseNodes.length
  // Save it in database
  node.dbIndex = this.mapIDs[id] = i     
  this.nDatabaseNodes[i] = node    
  var h = pnode.nodeChildren.length
  // Add it as child of it's parent
  node.siblingIndex = h
  pnode.nodeChildren[h] = node
}
// // 增加set方法，用于切换时保存，配合缓存
ECOTree.prototype.set = function (id, pid, dsc, type, typeText, wc, isEdt, w, h, c, bc, target, meta) {    
  
  for (var k = 0; k < this.nDatabaseNodes.length; k++)
  {
      if (this.nDatabaseNodes[k].id == id 
      // 新增无id节点
          || this.nDatabaseNodes[k].pid == id
      )
      {
        // 留着做拖拽？
          // this.nDatabaseNodes[k].parent = pid
        if(dsc!==null && undefined !== dsc) {
              this.nDatabaseNodes[k].dsc = dsc
        }
        if(wc!==null && undefined !== wc) {
              this.nDatabaseNodes[k].wc = wc
        }
          this.nDatabaseNodes[k].isEdt = false
          break
      }
  }    
      
}

ECOTree.prototype.searchNodes = function (str) {
  var node = null
  var m = this.config.searchMode
  var sm = (this.config.selectMode == ECOTree.SL_SINGLE)
  if (typeof str == "undefined") return
  if (str == "") return
  
  var found = false
  var n = (sm) ? this.iLastSearch : 0
  if (n == this.nDatabaseNodes.length) n = this.iLastSeach = 0
  
  str = str.toLocaleUpperCase()
  
  for (; n < this.nDatabaseNodes.length; n++)
  {         
      node = this.nDatabaseNodes[n]                
      if (node.dsc.toLocaleUpperCase().indexOf(str) != -1 && ((m == ECOTree.SM_DSC) || (m == ECOTree.SM_BOTH))) {
          node._setAncestorsExpanded()
          this._selectNodeInt(node.dbIndex, false)
          found = true
      }
      if (node.meta.toLocaleUpperCase().indexOf(str) != -1 && ((m == ECOTree.SM_META) || (m == ECOTree.SM_BOTH))) {
          node._setAncestorsExpanded()
          this._selectNodeInt(node.dbIndex, false)
          found = true
      }
      if (sm && found) {
          this.iLastSearch = n + 1
          break
      }
  }    
  this.UpdateTree()    
}

ECOTree.prototype.selectAll = function () {
  if (this.config.selectMode != ECOTree.SL_MULTIPLE) return
  this._selectAllInt(true)
}

ECOTree.prototype.unselectAll = function () {
  this._selectAllInt(false)
}

ECOTree.prototype.collapseAll = function () {
  this._collapseAllInt(true)
}

ECOTree.prototype.expandAll = function () {
  this._collapseAllInt(false)
}

ECOTree.prototype.collapseNode = function (nodeid, upd) {
  var dbindex = this.mapIDs[nodeid]
  this.nDatabaseNodes[dbindex].isCollapsed = !this.nDatabaseNodes[dbindex].isCollapsed
  if (upd) this.UpdateTree()
}

ECOTree.prototype.selectNode = function (nodeid, upd) {        
  this._selectNodeInt(this.mapIDs[nodeid], true)
  if (upd) this.UpdateTree()
}

//20181220
ECOTree.prototype.focusInput = function (e) {
  e.stopPropagation()
}

ECOTree.prototype.setNodeTitle = function (nodeid, title, upd) {
  var dbindex = this.mapIDs[nodeid]
  this.nDatabaseNodes[dbindex].dsc = title
  if (upd) this.UpdateTree()
}

ECOTree.prototype.setNodeMetadata = function (nodeid, meta, upd) {
  var dbindex = this.mapIDs[nodeid]
  this.nDatabaseNodes[dbindex].meta = meta
  if (upd) this.UpdateTree()
}

ECOTree.prototype.setNodeTarget = function (nodeid, target, upd) {
  var dbindex = this.mapIDs[nodeid]
  this.nDatabaseNodes[dbindex].target = target
  if (upd) this.UpdateTree()    
}

ECOTree.prototype.setNodeColors = function (nodeid, color, border, upd) {
  var dbindex = this.mapIDs[nodeid]
  if (color) this.nDatabaseNodes[dbindex].c = color
  if (border) this.nDatabaseNodes[dbindex].bc = border
  if (upd) this.UpdateTree()    
}

ECOTree.prototype.getSelectedNodes = function () {
  var node = null
  var selection = []
  var selnode = null    

  for (var n=0; n<this.nDatabaseNodes.length; n++) {
      node = this.nDatabaseNodes[n]
      if (node.isSelected)
      {            
          selnode = {
              "id" : node.id,
              "dsc" : node.dsc,
              "meta" : node.meta,
              "parent" : node.pid,
              // 20181225
              "weight": node.wc,
              "type": node.type,
              "fatherId": node.pid,
              "content": node.dsc,
              evaluationMethod: node.type,
              evaluationMethodText: node.typeText
          }
          selection[selection.length] = selnode
      }
  }
  return selection
}

export default ECOTree
